<?php

function _jsonrpc_json_encode(&$x, $prefix='', $max_depth = 8, &$out, $depth = 0, $parent_cond = FALSE) {
	$v =& $x;
	if ($v === NULL || is_scalar($v) ) {
		switch (TRUE) {
			case $v === NULL    : $out[] = 'null'  ; return;
			case $v === TRUE    : $out[] = 'true'  ; return;
			case $v === FALSE   : $out[] = 'false' ; return;
			case is_float($v)   :
			case (int)$v === $v : $out[] = $v; return;
		}
		$out[] = '"' . addcslashes($v, "\\\"\r\n\t") . '"';
		return;
	}

	if (!is_object($v) && !is_array($v)) {
		$out[] = 'null';
		return;
	}

	$i = 0;
	foreach ($x as $i => $_) break;
	if ($i === 0 && is_array($x)) {
		$max = count($x);
		$cond = FALSE;
		if ($prefix !== FALSE) {
			if ($max > 0 && $x[0] !== NULL && (!is_scalar($x[0]) || (is_string($x[0]) && strlen($x[0]) > 40))) $cond = TRUE;
			//if ($max > 1 && $x[1] !== NULL && (!is_scalar($x[1]) || (is_string($x[1]) && strlen($x[1]) > 40))) $cond = TRUE;
			//if ($max > 2 && $x[2] !== NULL && (!is_scalar($x[2]) || (is_string($x[2]) && strlen($x[2]) > 40))) $cond = TRUE;
			if ($max > 10) $cond = TRUE;
		}
		$out[] = '[';
		for ($i = 0; $i < $max; ++$i) {
			$v = &$x[$i];
			if ($i) $out[] = ', ';
			if ($v === NULL || is_scalar($v)) {
				if ($cond) $out[] = "\n$prefix    ";
				switch (TRUE) {
					case $v === NULL    : $out[] = 'null'  ; break;
					case $v === TRUE    : $out[] = 'true'  ; break;
					case $v === FALSE   : $out[] = 'false' ; break;
					case is_float($v) : case is_int($v):
					case is_numeric($v) && (string)(float)$v === $v : $out[] = $v; break;
					case is_string($v)  : $out[] = '"' . addcslashes($v, "\\\"\r\n\t") . '"'; break;
					default: $out[] = '"???"';
				}
			}
			elseif ($depth <= $max_depth) {
				//if ($max > 10)
					$out[] = "\n$prefix    ";
				if ((is_object($v) && isset($v->__locked)) || (is_array($v) && isset($v['__locked']))) continue;
				_jsonrpc_json_encode(&$v, $prefix===FALSE ? FALSE : "$prefix    ", $max_depth, &$out, $depth + 1, $cond);
			}
			else
				$out[] = '"***OVERFLOW***"';
		}
		if ($cond) $out[] = "\n$prefix";
		$out[] = ']';
		return;
	}
	else {
		$cond = FALSE;
		if ($prefix!==FALSE) {
			$cond = FALSE;
			$count = 0;
			foreach ($x as &$v)
				if (++$count > 3 || ($v!==NULL && !is_scalar($v))) {
					$cond = TRUE;
					break;
				}
		}
		if (is_object($x)) $x->__locked = TRUE; else $x['__locked'] = TRUE;
		//if (!$cond && $parent_cond) $out[] = "\n$prefix";
		$out[] = '{ ';
		$first = TRUE;
		$is_array = is_array($x);
		if ($is_array)
			$keys = array_keys($x);
		else {
			$keys = array();
			foreach ($x as $i => &$v)
				$keys[] = $i;
		}
		foreach ($keys as $i) {
			//if ($i=='' || substr($i, 0, 2) == '__') continue;
			if ($i=='' || $i == '__locked' || $i == '#index') continue;
			if ($is_array)
				$v =& $x[$i];
			else
				$v =& $x->$i;
			//if ($i=='' || left($i, 2) == '__') continue;
			if ((is_object($v) && isset($v->__locked)) || (is_array($v) && isset($v['__locked']))) continue;
			//if ($i[0]=='#') continue;
			if (!$first) $out[] = ', ';
			if ($cond)   $out[] = "\n$prefix    ";
			$out[] = '"' . addcslashes($i, "\0..\37\"") . '": ';
			if ($v === NULL || is_scalar($v)) {
				switch (TRUE) {
					case $v === NULL    : $out[] = 'null' ; break;
					case $v === TRUE    : $out[] = 'true' ; break;
					case $v === FALSE   : $out[] = 'false'; break;
					case is_float($v) : case is_int($v):
					case is_numeric($v) && (string)(float)$v === $v : $out[] = $v; break;
					//case is_int($v) :  $out[] = $v; break;
					case is_string($v)  : $out[] = '"' . addcslashes($v, "\\\"\r\n\t") . '"'; break;
					default: $out[] = '"???"';
				}
			}
			elseif ($depth <= $max_depth)
				_jsonrpc_json_encode(&$v, $prefix===FALSE ? FALSE : "$prefix    ", $max_depth, &$out, $depth + 1, $cond);
			else $out[] = '"***OVERFLOW***"';
			if ($first) $first = FALSE;
		}
		$out[] = $cond ? "\n$prefix}" : ' }';
		if (is_object($x)) unset($x->__locked); else unset($x['__locked']);
	}
}


/*
 *
 */
function jsonrpc_json_encode(&$x, $format=TRUE) {
	$prefix = $format ? '' : FALSE;
	$out = array();
	_jsonrpc_json_encode($x, &$prefix, 30, &$out);
	return join('', $out);
}
